(*
Copyright (c) 2008 Handel Information Technologies, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*)
namespace SQLGen

open System

type Table = struct
    val Database : string option
    val Owner : string option
    val TableName : string
    val Alias : string option
    static member Name: tableName : string -> Table
    static member NameAs: tableName : string * alias : string -> Table
    static member QualifiedName: database : string * owner : string * tableName : string -> Table
    static member QualifiedNameAs: database : string * owner : string * tableName : string * alias : string -> Table
    new: database : string option * owner : string option * tableName : string * alias : string option -> Table
end

type Rel = struct
    val parentTable : Table
    val parentField : string
    val childTable : Table
    val childField : string
    new : pTable : Table * pField : string * cTable : Table * cField : string -> Rel
end

type Val =
  | Field of string option * string
  | Parameter of string
  | ValExpression of string
  | StringLiteral of string
  | IntLiteral of int
  | FloatLiteral of float
  | BoolLiteral of bool
  | DateLiteral of DateTime
  | DateTimeLiteral of DateTime
  with 
    member EQ: operand : Val -> Where
    member EQFld: fieldName : string -> Where
    member EQFldT: tableName : string * fieldName : string -> Where
    member EQParam: parameterName : string -> Where
    member EQExpr: expression : string -> Where
    member EQStr: literal : string -> Where
    member EQInt: literal : int -> Where
    member EQFloat: literal : float -> Where
    member EQBool: literal : bool -> Where
    member EQDate: literal : System.DateTime -> Where
    member EQDateTime: literal : System.DateTime -> Where
    member NE: operand : Val -> Where
    member NEFld: fieldName : string -> Where
    member NEFldT: tableName : string * fieldName : string -> Where
    member NEParam: parameterName : string -> Where
    member NEExpr: expression : string -> Where
    member NEStr: literal : string -> Where
    member NEInt: literal : int -> Where
    member NEFloat: literal : float -> Where
    member NEBool: literal : bool -> Where
    member NEDate: literal : System.DateTime -> Where
    member NEDateTime: literal : System.DateTime -> Where
    member LT: operand : Val -> Where
    member LTFld: fieldName : string -> Where
    member LTFldT: tableName : string * fieldName : string -> Where
    member LTParam: parameterName : string -> Where
    member LTExpr: expression : string -> Where
    member LTStr: literal : string -> Where
    member LTInt: literal : int -> Where
    member LTFloat: literal : float -> Where
    member LTBool: literal : bool -> Where
    member LTDate: literal : System.DateTime -> Where
    member LTDateTime: literal : System.DateTime -> Where
    member LE: operand : Val -> Where
    member LEFld: fieldName : string -> Where
    member LEFldT: tableName : string * fieldName : string -> Where
    member LEParam: parameterName : string -> Where
    member LEExpr: expression : string -> Where
    member LEStr: literal : string -> Where
    member LEInt: literal : int -> Where
    member LEFloat: literal : float -> Where
    member LEBool: literal : bool -> Where
    member LEDate: literal : System.DateTime -> Where
    member LEDateTime: literal : System.DateTime -> Where
    member GT: operand : Val -> Where
    member GTFld: fieldName : string -> Where
    member GTFldT: tableName : string * fieldName : string -> Where
    member GTParam: parameterName : string -> Where
    member GTExpr: expression : string -> Where
    member GTStr: literal : string -> Where
    member GTInt: literal : int -> Where
    member GTFloat: literal : float -> Where
    member GTBool: literal : bool -> Where
    member GTDate: literal : System.DateTime -> Where
    member GTDateTime: literal : System.DateTime -> Where
    member GE: operand : Val -> Where
    member GEFld: fieldName : string -> Where
    member GEFldT: tableName : string * fieldName : string -> Where
    member GEParam: parameterName : string -> Where
    member GEExpr: expression : string -> Where
    member GEStr: literal : string -> Where
    member GEInt: literal : int -> Where
    member GEFloat: literal : float -> Where
    member GEBool: literal : bool -> Where
    member GEDate: literal : System.DateTime -> Where
    member GEDateTime: literal : System.DateTime -> Where
    member Like: operand : Val -> Where
    member LikeFld: fieldName : string -> Where
    member LikeFldT: tableName : string * fieldName : string -> Where
    member LikeParam: parameterName : string -> Where
    member LikeExpr: expression : string -> Where
    member LikeStr: literal : string -> Where
    member IsNull: Where
    member IsNotNull: Where
  end
and Comparator =
  | Equals of Val
  | NotEqual of Val
  | LessThan of Val
  | LessThanOrEqual of Val
  | GreaterThan of Val
  | GreaterThanOrEqual of Val
  | Like of Val
  | IsNull
  | IsNotNull
and Where =
  | All
  | FieldValueList of string * int array
  | SubQueryIn of Rel * Where
  | SubQueryExists of Rel * Where
  | And of Where * Where
  | Or of Where * Where
  | Comparison of Val * Comparator
  | SelExpression of string
  | Not of Where
  with
    member And: selection : Where -> Where
    member Or: selection : Where -> Where
    static member Fld: fieldName : string -> Val
    static member FldT: tableName : string * fieldName : string -> Val
    static member Param: parameterName : string -> Val
    static member Expr: expression : string -> Val
    static member Str: stringLiteral : string -> Val
    static member Int: intLiteral : int -> Val
    static member Float: floatLiteral : float -> Val
    static member Date: dateLiteral : DateTime -> Val
    static member DateTime: dateTimeLiteral : DateTime -> Val
  end

type JoinType =
  | Inner
  | Left
  | Right
  | Full

type From = 
  | NoTable
  | Simple of Table
  | JoinWith of From * JoinType * Rel 
  with
    member Join: jointype : JoinType * relationship : Rel -> From
    member InnerJoin: relationship : Rel -> From
    member InnerJoin: tableName : string * sourceField : string * targetField : string -> From
    member InnerJoinT: table : Table * sourceField : string * targetField : string -> From
    member LeftJoin: relationship : Rel -> From
    member LeftJoin: tableName : string * sourceField : string * targetField : string -> From
    member LeftJoinT: table : Table * sourceField : string * targetField : string -> From
    member RightJoin: relationship : Rel -> From
    member RightJoin: tableName : string * sourceField : string * targetField : string -> From
    member RightJoinT: table : Table * sourceField : string * targetField : string -> From
    member FullJoin: relationship : Rel -> From
    member FullJoin: tableName : string * sourceField : string * targetField : string -> From
    member FullJoinT: table : Table * sourceField : string * targetField : string -> From
    static member TableName: tableName : string -> From
    static member QualifiedTableName: database : string * owner : string * tableName : string -> From
    static member QualifiedTableNameAs: database : string * owner : string * tableName : string * alias : string -> From
  end
  
type Field = 
  | Literal of Table option * string * string option
  | CalcExpression of string * string option
  | Subquery of Field * From * Where * string option
  | FieldString of string
  with
    static member Name: fieldName : string -> Field
    static member NameAs: fieldName : string * alias : string -> Field
    static member TableName: table : Table * fieldName : string -> Field
    static member TablenameName: table : string * fieldName : string -> Field
    static member TableNameAs: table : Table * fieldName : string * alias : string -> Field
    static member TablenameNameAs: table : string * fieldName : string * alias : string -> Field
    static member Calc: expression : string -> Field
    static member CalcAs: expression : string * alias : string -> Field
  end


type Order =
  | Ascending
  | Descending

type Quantifier =
  | Any
  | Distinct
  | Top of int
  | TopPercent of int

type OrderBy = struct
    val field : string
    val order : Order
    new : fieldname : string * fieldorder : Order -> OrderBy
end

type GroupSplitType = 
  | NoSplit
  | Separate
  | Go

type Statement = 
  | Select of Field array * Quantifier * From * Where * string array * Where * OrderBy array
  | InsertVals of Table * (string * string) array
  | InsertSel of (string * Field) array * Table * Quantifier * From * Where
  | Update of Table * (string * string) array * From option * Where
  | Delete of From * Where
  | StoredProcedure of string * Val array
  | StatementGroup of Statement list * GroupSplitType * bool
with
    static member SimpleSel : fields : string array * from : From * where : Where -> Statement
    static member SimpleSelF : fields : Field array * from : From * where : Where -> Statement
    static member FullSel : fields : string array * quantifier : Quantifier * from : From * where : Where * orderBy : OrderBy array -> Statement
    static member FullSelF : fields : Field array * quantifier : Quantifier * from : From * where : Where * orderBy : OrderBy array -> Statement
    static member GroupSel : fields : string array * from : From * where : Where * groupBy : string array * having : Where * orderBy : OrderBy array -> Statement
    static member GroupSelF : fields : Field array * from : From * where : Where * groupBy : string array * having : Where * orderBy : OrderBy array -> Statement
    static member InsertFromSel : fields : string array * table : Table * selectStatement : Statement -> Statement
    static member InsertSel : fields : (string * string) array * table : Table * from : From * where : Where -> Statement
    static member InsertSelF : fields : (string * Field) array * table : Table * from : From * where : Where -> Statement
    static member SimpleUpdate : table : Table * setFields : (string * string) array * where : Where -> Statement
    static member FullUpdate : table : Table * setFields : (string * string) array * from : From * where : Where -> Statement
    static member Group : statements : Statement array * splitType: GroupSplitType * useTransaction: bool -> Statement
end

// to do: expose some intermediate options
module SqlModel = begin
  val emitStatementSql : Statement -> string
end

